﻿using System;
using GlmSharp;
using OpenGL;

namespace Trackball
{
    class Renderer
    {
        private Camera mCamera;
        private mat4 mProjection;

        public Renderer()
        {
            mCamera = new Camera();
        }

        public void init()
        {
            Gl.Enable(EnableCap.DepthTest);
            Gl.DepthFunc(DepthFunction.Less);
            Gl.Hint(HintTarget.PerspectiveCorrectionHint, HintMode.Nicest);
            Gl.Enable(EnableCap.Blend);
            Gl.BlendFunc(BlendingFactor.SrcAlpha, BlendingFactor.OneMinusSrcAlpha);
        }

        public void render()
        {
            renderClear();
            renderObjects();
        }

        public void setCamera(Camera c)
        {
            mCamera = c;
        }

        public Camera getCamera()
        {
            return mCamera;
        }

        public void renderCenter()
        {
            Gl.PointSize(4.0f);
            Gl.Begin(PrimitiveType.Points);

            if (mCamera != null)
            {
                vec3 p = mCamera.getCenter();
                Gl.Color3(1.0f, 1.0f, 0.0f);
                Gl.Vertex3(p.x, p.y, p.z);
            }

            Gl.End();
        }

        public void renderClear()
        {
            Gl.Clear(ClearBufferMask.ColorBufferBit | ClearBufferMask.DepthBufferBit);
            Gl.ClearColor(0.0f, 0.0f, 0.0f, 0.0f);
            Gl.ClearDepth(1.0f);
        }

        public void renderObjects()
        {
            Gl.PushMatrix();
            Gl.MultMatrix(mProjection.Values1D);

            if (mCamera != null)
            {
                Gl.PushMatrix();
                Gl.MultMatrix(mCamera.getMatrixFlat());
            }

            renderCenter();
            renderGrid();
            renderAxes();
            renderCube();

            if (mCamera != null)
            {
                Gl.PopMatrix();
            }
            Gl.PopMatrix();
        }

        public void renderAxes()
        {
            Gl.LineWidth(2.0f);

            Gl.Begin(PrimitiveType.Lines);
            Gl.Color3(1.0f, 0.0f, 0.0f);
            Gl.Vertex3(0.0f, 0.0f, 0.0f);
            Gl.Vertex3(0.5f, 0.0f, 0.0f);

            Gl.Color3(0.0f, 1.0f, 0.0f);
            Gl.Vertex3(0.0f, 0.0f, 0.0f);
            Gl.Vertex3(0.0f, 0.5f, 0.0f);

            Gl.Color3(0.0f, 0.0f, 1.0f);
            Gl.Vertex3(0.0f, 0.0f, 0.0f);
            Gl.Vertex3(0.0f, 0.0f, 0.5f);
            Gl.End();
        }

        public void renderCube()
        {
            float alpha = 0.9f;

            Gl.Begin(PrimitiveType.Triangles);

            Gl.Color4(0.8f, 0.4f, 0.4f, alpha);
            Gl.Vertex3(0.2f, 0.0f, 0.0f);
            Gl.Vertex3(0.2f, 0.2f, 0.0f);
            Gl.Vertex3(0.2f, 0.2f, 0.2f);
            Gl.Vertex3(0.2f, 0.0f, 0.0f);
            Gl.Vertex3(0.2f, 0.0f, 0.2f);
            Gl.Vertex3(0.2f, 0.2f, 0.2f);

            Gl.Color4(0.4f, 0.2f, 0.2f, alpha);
            Gl.Vertex3(0.0f, 0.0f, 0.0f);
            Gl.Vertex3(0.0f, 0.2f, 0.0f);
            Gl.Vertex3(0.0f, 0.2f, 0.2f);
            Gl.Vertex3(0.0f, 0.0f, 0.0f);
            Gl.Vertex3(0.0f, 0.0f, 0.2f);
            Gl.Vertex3(0.0f, 0.2f, 0.2f);

            Gl.Color4(0.4f, 0.8f, 0.4f, alpha);
            Gl.Vertex3(0.0f, 0.2f, 0.0f);
            Gl.Vertex3(0.2f, 0.2f, 0.0f);
            Gl.Vertex3(0.2f, 0.2f, 0.2f);
            Gl.Vertex3(0.0f, 0.2f, 0.0f);
            Gl.Vertex3(0.0f, 0.2f, 0.2f);
            Gl.Vertex3(0.2f, 0.2f, 0.2f);

            Gl.Color4(0.2f, 0.4f, 0.2f, alpha);
            Gl.Vertex3(0.0f, 0.0f, 0.0f);
            Gl.Vertex3(0.2f, 0.0f, 0.0f);
            Gl.Vertex3(0.2f, 0.0f, 0.2f);
            Gl.Vertex3(0.0f, 0.0f, 0.0f);
            Gl.Vertex3(0.0f, 0.0f, 0.2f);
            Gl.Vertex3(0.2f, 0.0f, 0.2f);

            Gl.Color4(0.4f, 0.4f, 0.8f, alpha);
            Gl.Vertex3(0.0f, 0.0f, 0.2f);
            Gl.Vertex3(0.0f, 0.2f, 0.2f);
            Gl.Vertex3(0.2f, 0.2f, 0.2f);
            Gl.Vertex3(0.0f, 0.0f, 0.2f);
            Gl.Vertex3(0.2f, 0.0f, 0.2f);
            Gl.Vertex3(0.2f, 0.2f, 0.2f);

            Gl.Color4(0.2f, 0.2f, .4f, alpha);
            Gl.Vertex3(0.0f, 0.0f, 0.0f);
            Gl.Vertex3(0.0f, 0.2f, 0.0f);
            Gl.Vertex3(0.2f, 0.2f, 0.0f);
            Gl.Vertex3(0.0f, 0.0f, 0.0f);
            Gl.Vertex3(0.2f, 0.0f, 0.0f);
            Gl.Vertex3(0.2f, 0.2f, 0.0f);

            Gl.End();
        }

        public void renderGrid()
        {
            float p, d = 0.1f, mn = -1.0f, mx = 1.0f, eps = -1e-4f;
            int n = 20;

            Gl.LineWidth(1.0f);
            Gl.Begin(PrimitiveType.Lines);

            for (int i = 0; i <= n; i++)
            {
                p = mn + i * d;
                if (i == 0 || i == 10 || i == n)
                {
                    Gl.Color4(0.0f, 1.0f, 0.0f, 0.3f);
                }
                else
                {
                    Gl.Color4(1.0f, 1.0f, 1.0f, 0.2f);
                }
                Gl.Vertex3(p, mn, eps);
                Gl.Vertex3(p, mx, eps);

                if (i == 0 || i == 10 || i == n)
                {
                    Gl.Color4(1.0f, 0.0f, 0.0f, 0.3f);
                }
                else
                {
                    Gl.Color4(1.0f, 1.0f, 1.0f, 0.2f);
                }
                Gl.Vertex3(mn, p, eps);
                Gl.Vertex3(mx, p, eps);
            }

            Gl.End();
        }

        public void resize(int width, int height)
        {
            if (0 == height)
            {
                height = 1; 
            }

            float ratio = (float)width / (float)height;
            Gl.Viewport(0, 0, width, height);
            Gl.MatrixMode(MatrixMode.Projection);
            Gl.LoadIdentity();

            mProjection = mat4.Perspective((float)Math.PI / 4, ratio, 0.01f, 100.0f);
        }
    }
}
